package com.rockwell.maze800.views;

import java.util.ArrayList;

import com.rockwell.maze800.PathActivity;
import com.rockwell.maze800.model.MazePath;
import com.rockwell.maze800.model.RobotAction;
import com.rockwell.maze800.model.RobotLocation;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Point;
import android.util.AttributeSet;
import android.view.View;

public class PathView extends View {
	private int m_mazeWidth = 16;
	private int m_mazeHeight = 16;
	
	private static final int ARROW_LEN = 5;
	
	/**
	 *  The position of the upper left corner of the view
	 */
	protected int m_left;
	protected int m_top;
	
	/**
	 * The size of the view.
	 */
	protected int m_width;
	protected int m_height;

	/**
	 * The size of a cell.
	 */
	protected int m_cellSize;

	protected Paint m_borderPaint;
	protected Paint m_bgPaint;
	protected Paint m_gridLinePaint;
	protected Paint m_pathPaint;
	
	private String m_pathString;
	private int m_initialX;
	private int m_initialY;
	private MazePath.Direction m_initialDirection;
	
	public class Junction {
		public int x;
		public int y;
		public RobotLocation junctionType;
		public RobotAction action;
	}
	
	private ArrayList<Junction> m_junctions = new ArrayList<Junction>();
	
	public PathView(Context context) {
		super(context);
	}
	
	public PathView(Context context, AttributeSet attrs) {
		super(context, attrs);
		
		initPaints();
	}

	public PathView(Context context, AttributeSet attrs, int defStyleAttr) {
		super(context, attrs, defStyleAttr);
		
		initPaints();
	}
	
	public void setMazeWidth(int width) {
		m_mazeWidth = width;
	}
	
	public void setMazeHeight(int height) {
		m_mazeHeight = height;
	}
	
	public void draw(String pathString, int initialX, int initialY, MazePath.Direction initialDir) {
		m_pathString = pathString;
		m_initialX = initialX;
		m_initialY = initialY;
		m_initialDirection = initialDir;
		
		generatePath();
		
		invalidate();
	}
	
	private void generatePath() {
		int x = m_initialX;
		int y = m_initialY;
		MazePath.Direction dir = m_initialDirection;

		ArrayList<Integer> distances = new ArrayList<Integer>();
		ArrayList<RobotLocation> junctions = new ArrayList<RobotLocation>();
		ArrayList<RobotAction> directions = new ArrayList<RobotAction>();
		
		m_junctions.clear();
		if (MazePath.parsePathString(m_pathString, distances, junctions, directions)) {
			Junction j = new Junction();
			j.x = x;
			j.y = y;
			j.junctionType = RobotLocation.InitialPosition;
			j.action = RobotAction.Jog;
			
			m_junctions.add(j);
			
			for (int i = 0; i < distances.size(); i++) {
				int dist = distances.get(i);
				RobotAction ra = directions.get(i);
				
				switch (dir) {
				case Left:
					x -= dist;
					
					switch (ra) {
					case Jog:
						dir = MazePath.Direction.Left;
						break;
						
					case TurnLeft:
						dir = MazePath.Direction.Down;
						break;
						
					case TurnRight:
						dir = MazePath.Direction.Up;
						break;
						
					case TurnBack:
						dir = MazePath.Direction.Right;
						break;
					}
					break;
					
				case Right:
					x += dist;
					
					switch (ra) {
					case Jog:
						dir = MazePath.Direction.Right;
						break;
						
					case TurnLeft:
						dir = MazePath.Direction.Up;
						break;
						
					case TurnRight:
						dir = MazePath.Direction.Down;
						break;
						
					case TurnBack:
						dir = MazePath.Direction.Left;
						break;
					}
					break;
					
				case Up:
					y -= dist;
					
					switch (ra) {
					case Jog:
						dir = MazePath.Direction.Up;
						break;
						
					case TurnLeft:
						dir = MazePath.Direction.Left;
						break;
						
					case TurnRight:
						dir = MazePath.Direction.Right;
						break;
						
					case TurnBack:
						dir = MazePath.Direction.Down;
						break;
					}
					break;
					
				case Down:
					y += dist;
					
					switch (ra) {
					case Jog:
						dir = MazePath.Direction.Down;
						break;
						
					case TurnLeft:
						dir = MazePath.Direction.Right;
						break;
						
					case TurnRight:
						dir = MazePath.Direction.Left;
						break;
						
					case TurnBack:
						dir = MazePath.Direction.Up;
						break;
					}
					break;
				}
				
				j = new Junction();
				j.x = x;
				j.y = y;
				j.junctionType = junctions.get(i);
				j.action = directions.get(i);
				m_junctions.add(j);
			}
			
			switch (dir) {
			case Left:
				x--;
				break;
				
			case Right:
				x++;
				break;
				
			case Up:
				y--;
				break;
				
			case Down:
				y++;
				break;
			}

			j = new Junction();
			j.x = x;
			j.y = y;
			m_junctions.add(j);
		}
	}
	
	protected void initPaints() {
		setFocusable(true);
		setFocusableInTouchMode(true);

		m_borderPaint = new Paint();
		m_borderPaint.setColor(Color.BLUE);
		m_borderPaint.setStrokeWidth(3);
		m_borderPaint.setStyle(Style.STROKE);
		
		m_bgPaint = new Paint();
		m_bgPaint.setColor(Color.LTGRAY);
		m_bgPaint.setStyle(Style.FILL);

		m_gridLinePaint = new Paint();
		m_gridLinePaint.setStrokeWidth(3);
		m_gridLinePaint.setColor(Color.DKGRAY);
		m_gridLinePaint.setStyle(Style.STROKE);

		m_pathPaint = new Paint();
		m_pathPaint.setStrokeWidth(3);
		m_pathPaint.setColor(Color.RED);
		m_pathPaint.setStyle(Style.STROKE);
	}
	
	@Override
	protected void onDraw(Canvas canvas) {
		drawMaze(canvas);
		drawPath(canvas);
	}
	
	private void drawMaze(Canvas canvas) {
		// draw the background
		canvas.drawRect(m_left - 1, m_top - 1, 
				m_left + m_width + 1, m_top + m_height + 1, m_bgPaint);
		
		canvas.drawRect(m_left - 2, m_top - 2, 
				m_left + m_width + 2, m_top + m_height + 2, m_borderPaint);
		
		/*
		canvas.drawLine(m_left - 1, m_top - 1, 
				m_left + m_width + 2, m_top - 1, m_borderPaint);
		canvas.drawLine(m_left - 1, m_top - 1, 
				m_left - 1, m_top + m_height + 2, m_borderPaint);
		canvas.drawLine(m_left + m_width + 2, m_top - 1, 
				m_left + m_width + 2, m_top + m_height + 2, m_borderPaint);
		canvas.drawLine(m_left - 1, m_top + m_height + 2, 
				m_left + m_width + 2, m_top + m_height + 2, m_borderPaint);
		*/
		
		// draw the board
		//canvas.drawRect(m_left, m_top, 
		//		m_left + m_width, m_top + m_height, m_gridLinePaint);
		

		for (int i = 0; i <= m_mazeHeight; i++) {
			// Horizontal line
			float y = m_top + i * m_cellSize;
			canvas.drawLine(m_left, y, m_left + m_width, y, m_gridLinePaint);
		}
		
		for (int i = 0; i <= m_mazeWidth; i++) {
			// Vertical line
			float x = m_left + i * m_cellSize;
			canvas.drawLine(x, m_top, x, m_top + m_height, m_gridLinePaint);
		}
	}
	
	private void drawPath(Canvas canvas) {
		if (m_junctions.size() == 0)
			return;
		
		int offset = m_cellSize / 4;
		
		int prevX = 0;
		int prevY = 0;
		
		for (int i = 0; i < m_junctions.size() - 1; i++) {
			int x0 = m_junctions.get(i).x;
			int y0 = m_junctions.get(i).y;
			int x1 = m_junctions.get(i+1).x;
			int y1 = m_junctions.get(i+1).y;
			
			int arrowX0 = 0;
			int arrowY0 = 0;
			int arrowX1 = 0;
			int arrowY1 = 0;
			
			if (y0 == y1) {
				if (x1 > x0) {
					// right
					x0 = x0 * m_cellSize + offset * 3;
					y0 = y0 * m_cellSize + offset;
					x1 = x1 * m_cellSize + offset;
					y1 = y1 * m_cellSize + offset;
					
					arrowX0 = x1 - ARROW_LEN;
					arrowY0 = y1 - ARROW_LEN;
					arrowX1 = x1 - ARROW_LEN;
					arrowY1 = y1 + ARROW_LEN;
				}
				else {
					// left
					x0 = x0 * m_cellSize + offset;
					y0 = y0 * m_cellSize + offset * 3;
					x1 = x1 * m_cellSize + offset * 3;
					y1 = y1 * m_cellSize + offset * 3;
					
					arrowX0 = x1 + ARROW_LEN;
					arrowY0 = y1 + ARROW_LEN;
					arrowX1 = x1 + ARROW_LEN;
					arrowY1 = y1 - ARROW_LEN;
				}
			}
			else {
				if (y1 > y0) {
					// down
					x0 = x0 * m_cellSize + offset * 3;
					y0 = y0 * m_cellSize + offset * 3;
					x1 = x1 * m_cellSize + offset * 3;
					y1 = y1 * m_cellSize + offset;
					
					arrowX0 = x1 + ARROW_LEN;
					arrowY0 = y1 - ARROW_LEN;
					arrowX1 = x1 - ARROW_LEN;
					arrowY1 = y1 - ARROW_LEN;
				}
				else {
					// up
					x0 = x0 * m_cellSize + offset;
					y0 = y0 * m_cellSize + offset;
					x1 = x1 * m_cellSize + offset;
					y1 = y1 * m_cellSize + offset * 3;
					
					arrowX0 = x1 - ARROW_LEN;
					arrowY0 = y1 + ARROW_LEN;
					arrowX1 = x1 + ARROW_LEN;
					arrowY1 = y1 + ARROW_LEN;
				}
			}
			
			if (i > 0) {
				canvas.drawLine(prevX, prevY, x0, y0, m_pathPaint);
			}

			canvas.drawLine(x0, y0, x1, y1, m_pathPaint);
			canvas.drawLine(arrowX0, arrowY0, x1, y1, m_pathPaint);
			canvas.drawLine(arrowX1, arrowY1, x1, y1, m_pathPaint);

			prevX = x1;
			prevY = y1;
		}
	}
	
	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
	    super.onMeasure(widthMeasureSpec, heightMeasureSpec);

	    int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
	    int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
	    
	    int size = Math.min(parentWidth, parentHeight);
	    this.setMeasuredDimension(size, size);
	}
	
    @Override 
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    	super.onSizeChanged(w, h, oldw, oldh);
    	
		calculateGridSize(w, h);
	}

    /**
     * Calculate the size of the puzzle on the screen.
     * @param w
     * @param h
     */
	private void calculateGridSize(int w, int h) {
		m_left = 0;
		m_top = 0;
    	m_width = w;
    	m_height = h;
    	
		double cellSize1 = m_width / m_mazeWidth;
		double cellSize2 = m_height / m_mazeHeight;
		
		m_cellSize = (int)Math.min(cellSize1, cellSize2);
		
		m_width = m_cellSize * m_mazeWidth;
		m_height = m_cellSize * m_mazeHeight;
		
		m_left = (w - m_width) / 2;
		m_top = (h - m_height) / 2;
	}
}
